package com.darkflame.client.gui;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.Logger;

import com.darkflame.client.SpiffyConnection;
import com.darkflame.client.SpiffyConnectionPanel;
import com.darkflame.client.SpiffyWidgetsLite.SpiffyDragPanel_Lite;
import com.darkflame.client.semantic.SSSNode;
import com.darkflame.client.semantic.SSSNodesWithCommonProperty;
import com.darkflame.client.semantic.SSSProperty;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

public class RelationshipVisualiser extends SpiffyDragPanel_Lite {

	static Logger Log = Logger.getLogger("RelationshipVisualiser");

	int CenterAbout = 9000;
	int InternalHeight = 9000;
	int InternalWidth = 18000;  //has to be really big as we cant expand more to the left later, only to the right
	
	
	private int highestX=InternalWidth-1000;
	private int lowestX=0;
	
	private static HashMap<SSSProperty,BasicNodeContainer> AllDisplayedNodes = new  HashMap<SSSProperty,BasicNodeContainer> ();

	private SpiffyConnectionPanel doddles = new SpiffyConnectionPanel();
	
	//in future we should probably load all the results first before adding children/parents
	//this should help make a neater layout, and allow a variable number of recursive grandchildren/grandparents
	public void populate (ArrayList<SSSNode> result){

		//exit if no result
		if (result.size()==0){
			return;
		}
		
		
		//fill space
		int step = (InternalHeight/(result.size()+1));

		//max step, so it isnt too big a gap.
		if (step>160){
			step=160;
		}


		super.clearAllWidgets();

		doddles.clearAllPaths();
		
		super.setLoading(true, "Loading Please Wait");
		
		
		
		doddles.setPixelSize(InternalWidth, InternalHeight);
		doddles.getElement().getStyle().setZIndex(-1);		
		this.addWidget(doddles,0,0);
		doddles.getParent().setSize("100%", "100%");
		AllDisplayedNodes.clear();

		populateFromClasses(result,this.getOffsetWidth()/2, step,300);

		Log.info("___________________________________________refreshing lines");
		doddles.refreshLines();

		//color all result nodes
		
		
		//remove the loading timer

		super.setLoading(false, "Loaded Please Wait");
		
		
		
		//resize if needed
		//if (highestX>InternalWidth){
		//	InternalWidth=highestX+1000;
		//	highestX=InternalWidth;
		//	super.setInternalSize(InternalWidth,InternalHeight);
		//	super.setMovementLimits(20, 20, InternalWidth-20, InternalHeight-20);
//
		//}
		Log.info("setMovementLimits1"); 
		super.setMovementLimits(20, 20, InternalWidth-20, InternalHeight-20);
		
		//Centre on nodeContainer
		centerOnNode(result.get(0));
		
	}

	/** centres the visualiser on the specified displayed node **/
	private void centerOnNode(SSSNode sssNode) {
		
		//get the container
		SSSProperty nodeAsProperty = SSSProperty.createSSSProperty(SSSNode.SubClassOf,sssNode);
		BasicNodeContainer nodescontainer = AllDisplayedNodes.get(nodeAsProperty);
		if (nodescontainer==null){
			Log.info("container not found"); 
		}
		Log.info("centering on_____"+nodescontainer.contentSummery());
		//Log.info("at_____"+nodescontainer.getElement().getOffsetLeft()+","+nodescontainer.getElement().getAbsoluteTop());
		
		int X = nodescontainer.getElement().getParentElement().getOffsetLeft();
		int Y = nodescontainer.getElement().getParentElement().getOffsetTop();   //.getAbsoluteTop();
		
		X=X+(nodescontainer.getOffsetWidth()/2);
		Y=Y+(nodescontainer.getOffsetHeight()/2);
		
		super.setViewToPos(X,Y);
		
	}

	public ArrayList<BasicNodeContainer> populateFromClasses (ArrayList<SSSNode> result, int Xcenter,int nodeXstep, int startFromThisY){


		//this visualiser  works with triplets, so we convert before processing
		ArrayList<SSSProperty> converted = convertToClassPropertys(result);


		return populate (converted, Xcenter, nodeXstep,  startFromThisY,true,true,"#33FF33");
		

	}

	private ArrayList<SSSProperty> convertToClassPropertys(
			ArrayList<SSSNode> result) {
		ArrayList<SSSProperty> converted = new 	ArrayList<SSSProperty> ();

		for (SSSNode ValueNode : result) {

			SSSProperty nodeAsProperty = SSSProperty.createSSSProperty(SSSNode.SubClassOf,ValueNode);
			converted.add(nodeAsProperty);

		}
		return converted;
	}

	public ArrayList<BasicNodeContainer> populate (ArrayList<SSSProperty> result, int Xcenter,int nodeYstep, int startFromY, boolean getSuperclasss,boolean getSubclasses,String colour){

		//keep track of highest and lowest X
		if ((CenterAbout+Xcenter)>highestX){
			highestX=(CenterAbout+Xcenter);
			
		}
		
		if ((CenterAbout+Xcenter)<lowestX){
			lowestX=(CenterAbout+Xcenter);
			
		}
		
		
		int centerx = Xcenter;				
		int step = nodeYstep;
		int i=-1;

		ArrayList<BasicNodeContainer> newlyCreatedNodes= new ArrayList<BasicNodeContainer>();

		//for now, we just put them in a list vertically down the page

		for (SSSProperty nodeAsProperty : result) {

			BasicNodeContainer newnode;

			boolean checkForParentsAndChildren = false;

			//if node is already displayed we dont create a new one, but reuse it
			if (AllDisplayedNodes.containsKey(nodeAsProperty)){

				Log.info("getting existing node container:"+nodeAsProperty.getValue().getPURI());

				newnode = AllDisplayedNodes.get(nodeAsProperty);
				
				

			} else {



				Log.info("making new node container:"+nodeAsProperty.getValue().getPURI());
				i++;
				newnode = new BasicNodeContainer(nodeAsProperty);	
				
				//if (colour.length()>2){
				//	newnode.getElement().getStyle().setBackgroundColor(colour);
				//}
				
				this.addWidget(newnode,CenterAbout+centerx,startFromY+(i*step));

				AllDisplayedNodes.put(nodeAsProperty,newnode);
				//we should only check newly created nodes
				checkForParentsAndChildren=true;

			}
			
			//ensure its coloured correctly
			if (colour.length()>2){
				newnode.setBackgroundColour(colour);
			}
			
			newlyCreatedNodes.add(newnode);

			//if node has parent, add it to the left
			//if (nodeAsProperty.getValue().getKnownDirectParentClasses().size()>0){
			if (checkForParentsAndChildren){
				
				if (getSuperclasss){
				ArrayList<SSSProperty> converted = convertToClassPropertys(nodeAsProperty.getValue().getKnownDirectParentClasses());
				ArrayList<SSSProperty> tparents = getTripletParents(nodeAsProperty);



				Log.info("parent classes to link:"+tparents.toString());

				converted.addAll(tparents);

				//sanity check: make sure parent class's dont contain the current node;

				converted.remove(nodeAsProperty);


				BasicNodeContainer nodesToLink[] = new BasicNodeContainer[converted.size()];


				nodesToLink = populate(converted,centerx-(350),(65+step/8),startFromY+(i*step),true,false,"").toArray(nodesToLink);				


				doddles.MakeSpiffyConnections(getDOMParents(nodesToLink),newnode.getParent());

				}

				if (getSubclasses){
				//}
				//if node has parent, add it to the left
				ArrayList<SSSProperty> convertedSubClasses = convertToClassPropertys(nodeAsProperty.getValue().getKnownDirectSubclassesOfThis());

					Log.info("subclasses to link:"+convertedSubClasses.toString());

					convertedSubClasses.remove(nodeAsProperty);
					//needs sanity check: make sure parent class's dont contain the current node;

					BasicNodeContainer nodesToLink2[] = new BasicNodeContainer[convertedSubClasses.size()];

					//nodesToLink2 = populateFromClasses(nodeAsProperty.getValue().getKnownDirectSubclassesOfThis(),centery+(350),(40+step/8),startFromX+(i*step)).toArray(nodesToLink);				
					nodesToLink2 = populate(convertedSubClasses,centerx+(350),(65+step/8),startFromY+(i*step),false,true,"").toArray(nodesToLink2);				



					doddles.MakeSpiffyConnectionsInv(getDOMParents(nodesToLink2),newnode.getParent());

					
					
					//int p=-1;
					//for (SSSNode parent : sssNode.getKnownDirectParentClasses()) {
					//	p++;
					//	BasicNodeContainer newparent = new BasicNodeContainer(parent);	
					//	this.add(newparent,centery+(300),100+(i*step)+((40)*p));

					//	SpiffyConnection nc = new SpiffyConnection(newparent,newnode);
					//}

				
				}

			}
		} 

		return newlyCreatedNodes;




	}

	//due to the way spiffydrag currently works, we need to get the parents of the subwidgets
	private Widget[] getDOMParents(BasicNodeContainer[] nodesToLink2) {

		Widget[] domParents = new  Widget[nodesToLink2.length];
		int i=0;

		for (BasicNodeContainer root : nodesToLink2) 
		{
			domParents[i] = root.getParent();
			i++;
		}


		return domParents;
	}

	private ArrayList<SSSProperty> getTripletParents(SSSProperty nodeAsProperty) {

		ArrayList<SSSProperty> tparents= new ArrayList<SSSProperty>();


		HashSet<SSSNodesWithCommonProperty> parentTripletSets = SSSNodesWithCommonProperty.getCommonPropertySetsContaining(nodeAsProperty.getValue().getPURI());
		for (SSSNodesWithCommonProperty CommonProperty : parentTripletSets) {

			SSSProperty prop = SSSProperty.createSSSProperty(CommonProperty.getCommonPrec(),CommonProperty.getCommonValue());
			tparents.add(prop);

		}
		return tparents;
	}


	public RelationshipVisualiser(){


		super();

		super.DisplayDebugBar(true);
		super.setBackgroundColour("#EEFFFF");
		
		super.setSize("100%", "900px");		
		
		super.setInternalSize(InternalWidth,InternalHeight);
		
		super.setMovementLimits(50, 20, InternalWidth-20, InternalHeight-20);

		super.setViewToPos(CenterAbout, 20);

	}





}
